
#include "WorkerModel.h"

WorkerModel::WorkerModel(QObject *parent)
    : QObject(parent)
    , m_id(0)
    , m_isInited(false)
    , m_localDirMonitor(nullptr)
    , m_webdav(nullptr)
    , m_webdavDirParser(nullptr)
    , m_authenicatedReply(nullptr)
    , m_runningTaskMaxCount(1)
    , m_loopCount(0)
{
    qRegisterMetaType<Error>("Error");
    qRegisterMetaType<WebdavCfg>("WebdavCfg");
    qRegisterMetaType<State>("State");
}

WorkerModel::~WorkerModel()
{
}

void WorkerModel::Init(const WebdavCfg &cfg)
{
    m_webdav = new QWebdav(this);;
    m_webdavDirParser = new QWebdavDirParser(this);

#ifdef __unix__
    m_localDirMonitor = new DirMonitorForLinux(this);
#elif defined Q_OS_WIN
    m_localDirMonitor = new DirMonitorForWin(this);
#endif
    QTimer *dispathTimer = new QTimer(this);
    dispathTimer->setInterval(1000);

    connect(m_webdav, &QWebdav::errorChanged, this, &WorkerModel::onWebdavErrorChanged);
    // connect(w, &QWebdav::authenticationRequired, this, &WorkerModel::onWebdavAuthenticationRequired);
    connect(m_webdavDirParser, &QWebdavDirParser::finished, this, &WorkerModel::onDirParserFinished);
    connect(m_webdavDirParser, &QWebdavDirParser::errorChanged, this, &WorkerModel::onDirParserErrorChanged);
    // file monitor
    connect(m_localDirMonitor, &DirMonitor::FileClosed, this, &WorkerModel::onLocalFileClosed);
    connect(m_localDirMonitor, &DirMonitor::FileMovedOut, this, &WorkerModel::onLocalFileMovedOut);
    connect(m_localDirMonitor, &DirMonitor::FileMovedIn, this, &WorkerModel::onLocalFileMovedIn);
    connect(m_localDirMonitor, &DirMonitor::FileCreated, this, &WorkerModel::onLocalFileCreated);
    connect(m_localDirMonitor, &DirMonitor::FileDeleted, this, &WorkerModel::onLocalFileDeleted);
    // dispath
    connect(dispathTimer, &QTimer::timeout, this, &WorkerModel::dispathTask);

    // post init
    m_runningTaskMaxCount = 5;

    setWebdavCfg(cfg);

    dispathTimer->start();
}

void WorkerModel::SetId(int id)
{
    m_id = id;
}

int WorkerModel::GetId()
{
    return m_id;
}

void WorkerModel::OnReqSetWebdavCfg(int id, const WorkerModel::WebdavCfg &cfg)
{
    if (m_id != id) {
        return;
    }

    bool succeed = setWebdavCfg(cfg);

    Q_EMIT SetWebdavCfgFinished(succeed, cfg);
}

void WorkerModel::onWebdavAuthenticationRequired(QNetworkReply* reply, QAuthenticator* authenticator){
    qDebug() << Q_FUNC_INFO;

    if (reply == m_authenicatedReply) {
        reply->abort();
        reply->deleteLater();
        reply = 0;
    }
    m_authenicatedReply = reply;

    QVariantHash opts = authenticator->options();
    QVariant optVar;
    foreach(optVar, opts) {
        qDebug() << Q_FUNC_INFO << "option ==" << optVar.toString();
    }
    authenticator->setUser(m_webdavCfg.UserName);
    authenticator->setPassword(m_webdavCfg.Password);
    return;
}

void WorkerModel::onWebdavErrorChanged(QString errorMsg)
{
    printError(errorMsg);
}

void WorkerModel::onDirParserErrorChanged(QString errorMsg)
{
    printError(errorMsg);
}

void WorkerModel::onDirParserFinished()
{
    qDebug() << Q_FUNC_INFO;

    m_mapOfPathToWebdavItem.clear();
    const QList<QWebdavItem> & list = m_webdavDirParser->getList();
    for (const QWebdavItem &item : list) {
        m_mapOfPathToWebdavItem.insert(item.path(), item);
    }
}

void WorkerModel::onReplyUploadProgressChanged(Task *task, qint64 bytesSent, qint64 bytesTotal)
{
    qDebug() << Q_FUNC_INFO << task->LocalFilePath << bytesSent << bytesTotal;
}

void WorkerModel::onReplyFinished(Task *task)
{
    qDebug() << Q_FUNC_INFO << task->Type << task->LocalFilePath << task->SrvFilePath;

    if (task->ReplyPtr) {
        task->ReplyPtr->close();
        task->ReplyPtr->deleteLater();
        task->ReplyPtr = nullptr;
    }
    if (task->Type == Upload && task->LocalFilePtr) {
        task->LocalFilePtr->close();
        task->LocalFilePtr->deleteLater();
        task->LocalFilePtr = nullptr;
    }

    if (task->Type == Download) {
        // QWebdav put 上传文件，完成后会自动关闭并销毁文件，所以此处无需处理
        QFile f(task->LocalFilePath);
        if (f.exists()) {
            const QDateTime &dt = task->FileLastModifiedTime;
            if (!dt.isNull()) {
                if (f.open(QIODevice::OpenModeFlag::ReadWrite)) {
                    ChangeTimeInfo info;
                    info.Path = task->LocalFilePath;
                    info.Done = true;
                    // 下次文件关闭时，忽略对文件的后续操作
                    m_changeTimeInfoList.append(info);
                    f.setFileTime(dt, QFileDevice::FileTime::FileModificationTime);
                    f.close();
                } else {
                    // 存在下载完成但文件没关闭的情况。
                    ChangeTimeInfo info;
                    info.Path = task->LocalFilePath;
                    info.LastModifiedTime = task->FileLastModifiedTime;
                    m_changeTimeInfoList.append(info);
                }
            }
        }
    }

    if (task->Type == Rm) {
        m_mapOfPathToWebdavItem.remove(task->SrvFilePath);
    }

    // 移除已完成的任务
    m_runningTaskList.removeOne(task);
    delete task;
}

void WorkerModel::onReplyError(Task *task, QNetworkReply::NetworkError error)
{
    qDebug() << Q_FUNC_INFO << task->LocalFilePath << task->SrvFilePath << error;
}

void WorkerModel::onReplyDownloadProgressChanged(Task *task, qint64 bytesSent, qint64 bytesTotal)
{
    qDebug() << Q_FUNC_INFO << task->LocalFilePath << bytesSent << bytesTotal;
}

void WorkerModel::onLocalFileClosed(const QString &path)
{
    qDebug() << Q_FUNC_INFO << path;
    QString syncCfgDirPathInLocal = QDir::cleanPath(m_localDir.path() + QDir::separator() + SyncCfgDirRelativePath);
    if (path.startsWith(syncCfgDirPathInLocal)) {
        return;
    }

    for (int i = 0; i < m_changeTimeInfoList.size(); i++) {
        ChangeTimeInfo info = m_changeTimeInfoList.at(i);
        if (info.Path != path) {
            continue;
        }
        if (info.Done) {
            m_changeTimeInfoList.removeAt(i);
            return;
        }

        QFile f(path);
        if (f.open(QIODevice::OpenModeFlag::ReadWrite)) {
            f.setFileTime(info.LastModifiedTime, QFileDevice::FileTime::FileModificationTime);
            f.close();
        } else {
            qCritical() << Q_FUNC_INFO << path << "change FileModificationTime failed";
        }
        info.Done = true;
        m_changeTimeInfoList[i] = info;
        return;
    }

     // 确保不是正在下载的文件
//     for (const Task *t : m_runningTaskList) {
//         if (path == t->LocalFilePath) {
//             return;
//         }
//     }

    // 确保文件已经存在
    if (!m_localFilePathList.contains(path)) {
        m_localFilePathList.append(path);
        // sort
        m_localFilePathList.sort();

        // upload file list
        asyncUploadPathListCfg(m_localFilePathList);
    }

    addTask(Upload, path, "");
}

void WorkerModel::onLocalFileMovedOut(const QString &path)
{
    onLocalFileDeleted(path);
}

void WorkerModel::onLocalFileMovedIn(const QString &path)
{
    qDebug() << Q_FUNC_INFO << path;
    QString syncCfgDirPathInLocal = QDir::cleanPath(m_localDir.path() + QDir::separator() + SyncCfgDirRelativePath);
    if (path.startsWith(syncCfgDirPathInLocal)) {
        return;
    }

    addTask(Upload, path, "");
    // 上传文件列表
    if (!m_localFilePathList.contains(path)) {
        m_localFilePathList.append(path);
        // sort
        m_localFilePathList.sort();

        // upload file list
        asyncUploadPathListCfg(m_localFilePathList);
    }
}

void WorkerModel::onLocalFileCreated(const QString &path)
{
    qDebug() << Q_FUNC_INFO << path;
    QString syncCfgDirPathInLocal = QDir::cleanPath(m_localDir.path() + QDir::separator() + SyncCfgDirRelativePath);
    if (path.startsWith(syncCfgDirPathInLocal)) {
        return;
    }

    // 上传文件列表
    if (!m_localFilePathList.contains(path)) {
        m_localFilePathList.append(path);
        // sort
        m_localFilePathList.sort();

        // upload file list
        asyncUploadPathListCfg(m_localFilePathList);

        addTask(Upload, path, "");
    }
}

void WorkerModel::onLocalFileDeleted(const QString &path)
{
    qDebug() << Q_FUNC_INFO << path;
    QString syncCfgDirPathInLocal = QDir::cleanPath(m_localDir.path() + QDir::separator() + SyncCfgDirRelativePath);
    if (path.startsWith(syncCfgDirPathInLocal)) {
        return;
    }

    m_localFilePathList.removeAll(path);
    // rm srv file
    QString relativePath = m_localDir.relativeFilePath(path);
    QString srvFilePath = QDir::cleanPath(m_srvDir.path() + QDir::separator() + relativePath);
    addTask(Rm, "", srvFilePath);

    // sort
    m_localFilePathList.sort();

    // upload file list
    asyncUploadPathListCfg(m_localFilePathList);
}

bool WorkerModel::setWebdavCfg(const WebdavCfg &cfg)
{
    if (!QFileInfo(cfg.LocalDirPath).exists()) {
        qCritical() << Q_FUNC_INFO << "cfg is not correct!";
        return false;
    }
    for (Task *task : m_taskList) {
        delete task;
    }
    m_taskList.clear();

    m_webdavCfg = cfg;
    m_webdav->setConnectionSettings(m_webdavCfg.ConnType, m_webdavCfg.HostName, m_webdavCfg.RootPath, m_webdavCfg.UserName,
                                    m_webdavCfg.Password, m_webdavCfg.Port);

    m_localDir.setPath(m_webdavCfg.LocalDirPath);
    m_srvDir.setPath(m_webdavCfg.RemoteDirPath);

    m_localDirMonitor->SetRootDirPath(m_webdavCfg.LocalDirPath);

    initialAsync();

    return true;
}

void WorkerModel::initialAsync()
{
    // 获取服务器文件列表
    QString filePathListCfgFilePathInSrv = QDir::cleanPath(m_srvDir.path() + QDir::separator() + FilePathListCfgFilePath);
    QString filePathListCfgFilePathInLocal = QDir::cleanPath(m_localDir.path() + QDir::separator() + FilePathListCfgFilePath);
    QFileInfo fInfo(filePathListCfgFilePathInLocal);
    if (!fInfo.dir().exists()) {
        QDir().mkpath(fInfo.dir().path());
    }

    QList<QString> pathList;
    const QWebdavItem &item = syncGetSrvFileInfo(filePathListCfgFilePathInSrv);
    if (!item.path().isEmpty()) {
        syncDownloadFile(filePathListCfgFilePathInSrv);

        QFile f(filePathListCfgFilePathInLocal);
        f.open(QIODevice::OpenModeFlag::ReadOnly);
        const QString &content = f.readAll();
        f.close();

        pathList = content.split("\n");
        pathList.removeAll("");
    }
    // 若服务器文件列表为空
    if (pathList.isEmpty()){
        m_localFilePathList = getDirAllFilePathList(m_localDir.path());
        // upload file list
        asyncUploadPathListCfg(m_localFilePathList);
        // upload file in list
        for (const QString &path : m_localFilePathList) {
            addTask(Upload, path, "");
        }
    } else {
        // delete file which not exsit in list
        QList<QString> localFileAbsolutePathList = getDirAllFilePathList(m_localDir.path());
        for (const QString &path : localFileAbsolutePathList) {
            QString relativePath = m_localDir.relativeFilePath(path);
            if (QFileInfo(path).isDir()) {
                relativePath += "/";
            }
            if (!pathList.contains(relativePath)) {
                deleteLocalFileOrDir(path);
            }
        }
        // set m_localFilePathList
        m_localFilePathList.clear();
        for (const QString &path : pathList) {
            QString localFilePath = QDir::cleanPath(m_localDir.path() + QDir::separator() + path);
            m_localFilePathList.append(localFilePath);
        }
        // download file in list
        for (const QString &path : pathList) {
            QString srvFilePath = QDir::cleanPath(m_srvDir.path() + QDir::separator() + path);
            if (path.endsWith("/")) {
                srvFilePath += "/";
            }
            addTask(Download, "", srvFilePath);
        }
    }
}

void WorkerModel::printError(QString errorMsg)
{
    qDebug() << Q_FUNC_INFO << errorMsg;
}

void WorkerModel::asyncUploadFile(Task *task, const QString &path)
{
    QFileInfo fInfo(path);
    QString relativePath = m_localDir.relativeFilePath(path);
    QString srvFilePath = QDir::cleanPath(m_srvDir.path() + QDir::separator() + relativePath);
    task->SrvFilePath = srvFilePath;

    // 判断待上传的路径是否为目录，则服务器文件路径末尾需加“/”
    if (fInfo.isDir()) {
        srvFilePath += "/";
    }

    QNetworkReply *reply = nullptr;
    const QWebdavItem &item = syncGetSrvFileInfo(srvFilePath);
    if (fInfo.isDir()) {
        // 若待上传的路径是为目录，则判断是否需要创建目录
        if (!item.path().isEmpty()) {
            QTimer::singleShot(1, this, [this, task] {
                onReplyFinished(task);
            });
            return;
        }
        // 在服务器创建目录
        reply = m_webdav->mkdir(srvFilePath);

        task->ReplyPtr = reply;
        task->LocalFilePtr = nullptr;
    } else {
        // 若待上传的路径是为目录，则上传文件
        // 判断本地文件与服务器文件是否一样，一样则无需上传
        if (!item.path().isEmpty()) {
            if (isSrvFileSameToLocalFile(item, path)) {
                qInfo() << Q_FUNC_INFO << path << "has not modified, no need upload!";

                QTimer::singleShot(1, this, [this, task] {
                    onReplyFinished(task);
                });
                return;
            }
        }

        // 上传文件
        QFile *f = new QFile(path);
        if (!f->exists()) {
            QTimer::singleShot(1, this, [this, task] {
                onReplyFinished(task);
            });
            return;
        }
        f->open(QIODevice::OpenModeFlag::ReadOnly);
        reply = m_webdav->UploadFile(srvFilePath, f);

        task->ReplyPtr = reply;
        task->LocalFilePtr = f;
    }

    connect(reply, &QNetworkReply::uploadProgress, this, [this, task](qint64 bytesSent, qint64 bytesTotal) {
        this->onReplyUploadProgressChanged(task, bytesSent, bytesTotal);
    });
    connect(reply, &QNetworkReply::finished, this, [this, task]() {
        this->onReplyFinished(task);
    });
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
    connect(reply, &QNetworkReply::errorOccurred, this, [this, task](QNetworkReply::NetworkError error) {
        this->onReplyError(task, error);
    });
#else
    void (QNetworkReply::*signal)(QNetworkReply::NetworkError) = &QNetworkReply::error;
    connect(reply, signal, this, [this, task](QNetworkReply::NetworkError error) {
        this->onReplyError(task, error);
    });
#endif
}

void WorkerModel::asyncUploadDir(const QString &dirPath)
{
    const QStringList &filePathList = getDirAllFilePathList(dirPath);

    for (const QString &path : filePathList) {
        addTask(Upload, path, "");
    }
}

QStringList WorkerModel::getDirAllFilePathList(const QString &path)
{
    QStringList filePathList;
    QFileInfo info(path);
    if (info.exists()) {
        if (info.isFile()) {
            QString filePath = info.absoluteFilePath();
            filePathList.append(filePath);
        } else if (info.isDir()) {
            QString filePath = info.absoluteFilePath();
            filePathList.append(filePath);
            QDir dir(path);
            for (const QFileInfo &i : dir.entryInfoList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs | QDir::Hidden | QDir::NoSymLinks)) {
                // 递归扫描目录内容
                const QStringList &filePathListTmp = getDirAllFilePathList(i.absoluteFilePath());
                filePathList.append(filePathListTmp);
            }
        }
    } else {
        qCritical() << Q_FUNC_INFO << path << "not exists!";
    }

    return filePathList;
}

void WorkerModel::syncDownloadFile(const QString &srvFilePath)
{
    const QString &localFilePath = m_localDir.path() + QDir::separator() + m_srvDir.relativeFilePath(srvFilePath);
    const QWebdavItem &item = syncGetSrvFileInfo(srvFilePath);
    if (!item.path().isEmpty()) {
        if (isSrvFileSameToLocalFile(item, localFilePath)) {
            qInfo() << Q_FUNC_INFO << srvFilePath << "has not modified, no need download!";
            return;
        }
    }

    QFile *f = new QFile(localFilePath);
    f->open(QIODevice::OpenModeFlag::WriteOnly);
    QNetworkReply *reply = m_webdav->get(srvFilePath, f);

    QEventLoop loop;
    connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
    loop.exec();
    reply->deleteLater();

    if (!item.path().isEmpty()) {
        QDateTime dt = item.lastModified();
        dt.setTimeZone(QTimeZone::utc());
        f = new QFile(localFilePath);
        f->open(QIODevice::OpenModeFlag::ReadWrite);
        f->setFileTime(dt, QFileDevice::FileTime::FileModificationTime);
        f->close();
    }
}

void WorkerModel::asyncDownloadFile(Task *task, const QString &srvFilePath)
{
    const QString &localFilePath = m_localDir.path() + QDir::separator() + m_srvDir.relativeFilePath(srvFilePath);
    task->LocalFilePath = localFilePath;

    const QWebdavItem &item = syncGetSrvFileInfo(srvFilePath);
    if (!item.path().isEmpty()) {
        if (isSrvFileSameToLocalFile(item, localFilePath)) {
            qInfo() << Q_FUNC_INFO << srvFilePath << "has not modified, no need download!";

            QTimer::singleShot(1, this, [this, task] {
                onReplyFinished(task);
            });
            return;
        }

        if (item.isDir()) {
            QDir().mkpath(localFilePath);
            QTimer::singleShot(1, this, [this, task] {
                onReplyFinished(task);
            });
            return;
        }
    } else {
        qCritical() << Q_FUNC_INFO << srvFilePath << "not exist in srv!";
        QTimer::singleShot(1, this, [this, task] {
            onReplyFinished(task);
        });
        return;
    }

    QFileInfo fInfo(localFilePath);
    if (!fInfo.dir().exists()) {
        QDir().mkpath(fInfo.dir().path());
    }

    QFile *f = new QFile(localFilePath);
    f->open(QIODevice::OpenModeFlag::WriteOnly);
    QNetworkReply *reply = m_webdav->get(srvFilePath, f);

    task->ReplyPtr = reply;
    task->LocalFilePtr = f;
    if (!item.path().isEmpty()) {
        QDateTime dt = item.lastModified();
        dt.setTimeZone(QTimeZone::utc());
        task->FileLastModifiedTime = dt;
    }

    connect(reply, &QNetworkReply::downloadProgress, this, [this, task](qint64 bytesSent, qint64 bytesTotal) {
        this->onReplyDownloadProgressChanged(task, bytesSent, bytesTotal);
    });
    connect(reply, &QNetworkReply::finished, this, [this, task]() {
        this->onReplyFinished(task);
    });
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
    connect(reply, &QNetworkReply::errorOccurred, this, [this, task](QNetworkReply::NetworkError error) {
        this->onReplyError(task, error);
    });
#else
    void (QNetworkReply::*signal)(QNetworkReply::NetworkError) = &QNetworkReply::error;
    connect(reply, signal, this, [this, task](QNetworkReply::NetworkError error) {
        this->onReplyError(task, error);
    });
#endif
}

void WorkerModel::asyncRmSrvFile(Task *task, const QString &path)
{
    QNetworkReply *reply = m_webdav->remove(path);
    task->Type = Rm;
    task->ReplyPtr = reply;
    task->SrvFilePath = path;

    connect(reply, &QNetworkReply::finished, this, [this, task]() {
        this->onReplyFinished(task);
    });
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
    connect(reply, &QNetworkReply::errorOccurred, this, [this, task](QNetworkReply::NetworkError error) {
        this->onReplyError(task, error);
    });
#else
    void (QNetworkReply::*signal)(QNetworkReply::NetworkError) = &QNetworkReply::error;
    connect(reply, signal, this, [this, task](QNetworkReply::NetworkError error) {
        this->onReplyError(task, error);
    });
#endif
}

QWebdavItem WorkerModel::syncGetSrvFileInfo(const QString &srvFilePath)
{
    ++m_loopCount;
    QEventLoop loop;
    connect(m_webdavDirParser, &QWebdavDirParser::finished, &loop, &QEventLoop::quit);

    bool succeed = m_webdavDirParser->listItem(m_webdav, srvFilePath);
    if (succeed) {
        loop.exec();
    }
    --m_loopCount;

    const QWebdavItem &item = m_mapOfPathToWebdavItem.value(srvFilePath);
    return item;
}

bool WorkerModel::isSrvFileSameToLocalFile(const QWebdavItem &item, const QString &localFilePath)
{
    QFileInfo localFileInfo(localFilePath);
    if (!localFileInfo.exists()) {
        return false;
    }

    QDateTime dt = item.lastModified();
    dt.setTimeZone(QTimeZone::utc());
    dt = dt.toLocalTime();
    if (dt.toTime_t() == localFileInfo.lastModified().toTime_t()
        and item.size() == quint64(localFileInfo.size())
        ) {
        return true;
    }

    return false;
}

void WorkerModel::deleteLocalFileOrDir(const QString &path)
{
    QFileInfo fInfo(path);
    if (fInfo.isFile()) {
        QFile f(path);
        if (!f.remove()) {
            qCritical() << Q_FUNC_INFO << path << "delete failed!";
        }
    } else {
        QDir().rmpath(path);
    }
}

void WorkerModel::asyncUploadPathListCfg(const QList<QString> &pathList)
{
    QList<QString> pathListTmp;
    for (const QString &path : pathList) {
        QString relativePath = m_localDir.relativeFilePath(path);
        QFileInfo fInfo(path);
        if (fInfo.isDir()) {
            relativePath += "/";
        }
        pathListTmp.append(relativePath);
    }
    const QByteArray &contentNeedWrite = pathListTmp.join("\n").toUtf8();

    QString filePathListCfgFilePathInLocal = QDir::cleanPath(m_localDir.path() + QDir::separator() + FilePathListCfgFilePath);
    QFile f(filePathListCfgFilePathInLocal);
    if (!f.open(QIODevice::OpenModeFlag::WriteOnly)) {
        qCritical() << Q_FUNC_INFO << filePathListCfgFilePathInLocal << "open failed!";
        return;
    }
    f.write(contentNeedWrite);
    f.close();
    // upload file list
    addTask(Upload, filePathListCfgFilePathInLocal, "");
}

void WorkerModel::addTask(TaskType type, const QString &localFilePath, const QString &srvFilePath)
{
    for (const Task *t : m_taskList) {
        if (localFilePath == t->LocalFilePath
            && srvFilePath == t->SrvFilePath
            ) {
            qDebug() << Q_FUNC_INFO << "exist same task in task list:" << t->Type << t->LocalFilePath << t->SrvFilePath;
            return;
        }
    }

    // add
    Task *task = new Task();
    task->Type = type;
    task->LocalFilePath = localFilePath;
    task->SrvFilePath = srvFilePath;
    m_taskList.append(task);
}

void WorkerModel::dispathTask()
{
    // judge state
    State state = m_runningTaskList.isEmpty() ? Normal : Busy;
    if (state != m_state) {
        m_state = state;
        Q_EMIT StateChanged(state);
    }

    if (m_loopCount > 0) {
        return;
    }

    while (m_runningTaskList.size() < m_runningTaskMaxCount) {
        if (m_taskList.isEmpty()) {
            break;
        }
        Task *task = m_taskList.first();
        m_taskList.pop_front();

        // 当存在正在下载此文件的任务时，忽略对此文件的后续操作
        // 当存在正在上传或删除此文件的任务时，先取消此任务
        for (const Task *t : m_runningTaskList) {
            if (t->LocalFilePath == task->LocalFilePath && t->Type == Download) {
                qInfo() << Q_FUNC_INFO << task->LocalFilePath << "is downloading, ignore operatiing for this file";
                delete task;
                task = nullptr;
                break;
            }
            if (t->LocalFilePath == task->LocalFilePath
                || t->SrvFilePath == task->SrvFilePath
                ) {
                ++m_loopCount;
                QEventLoop loop;
                QTimer::singleShot(1, this, [t] {
                    t->ReplyPtr->abort();
                });
                connect(t->ReplyPtr, &QNetworkReply::finished, &loop, &QEventLoop::quit);
                loop.exec();
                --m_loopCount;
            }
        }
        if (task == nullptr) {
            continue;
        }

        m_runningTaskList.append(task);
        if (task->Type == Upload) {
            asyncUploadFile(task, task->LocalFilePath);
        } else if (task->Type == Download) {
            asyncDownloadFile(task, task->SrvFilePath);
        } else if (task->Type == Rm) {
            asyncRmSrvFile(task, task->SrvFilePath);
        } else {
            qCritical() << Q_FUNC_INFO << task->Type << "not support task type!";
            continue;
        }

    }
}
